#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif
#include <unistd.h>
#include <string>
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
//#include <rwlock.h>

#include "sqlite3.h"
//using namespace std;

class Log {
public:
    static void log(const std::string& msg) {
        std::lock_guard<std::mutex> _(lock_);
        std::cout << "[";
        std::cout << std::this_thread::get_id();
        std::cout << "] ";
        std::cout << msg;
        std::cout << std::endl;
    }
protected:
    static std::mutex lock_;
};
std::mutex Log::lock_;

class SqliteError : public std::exception {
public:
    explicit SqliteError(int error_code) : m_errno(error_code) {
        m_errmsg=sqlite3_errstr(error_code);
    }
    explicit SqliteError(sqlite3* db) {
        m_errno=sqlite3_errcode(db);
        m_errmsg=sqlite3_errmsg(db);
    }
    SqliteError(const SqliteError& src) = default;
    virtual ~SqliteError() throw() { }
    virtual const char* what() const throw() override { return m_errmsg; }
    int code() const throw() { return m_errno; }
private:
    int m_errno;
    const char* m_errmsg;
};

class Sqlite {
public:
    Sqlite() { sqlite_open("userdata.db"); }
    ~Sqlite() { sqlite_close(); }
public:
    void open(const char *file) {
        int result=sqlite3_open_v2(file, &m_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
        if(result!=SQLITE_OK)
            throw SqliteError(result);
    }
    void close() {
        if(m_db) {
            sqlite3_close_v2(m_db);
            m_db=NULL;
        }
    }
    void simple_execute(const char* lpszSql) {
        int result=sqlite3_exec(m_db, lpszSql, NULL, NULL, NULL);
        if(result!=SQLITE_OK)
            throw SqliteError(result);
    }
    void begin_transaction() {
        simple_execute("BEGIN TRANSACTION");
    }
    void commit() {
        simple_execute("COMMIT TRANSACTION");
    }
    void rollback() {
        simple_execute("ROLLBACK TRANSACTION");
    }
    bool table_exist(const std::string& table) {
        std::string sql = "SELECT * FROM sqlite_master WHERE type ='table' and name ='";
        sql += table;
        sql += "'";

        bool exist = false;
        sqlite3_stmt* stmt = NULL;
        if (sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &stmt, NULL) == SQLITE_OK) {
            int r = sqlite3_step(stmt);
            exist = r == SQLITE_ROW;
        }
        sqlite3_finalize(stmt);

        return exist;
    }
public:
    bool init_sqlite() {
        if (table_exist("user")) {
            //    Log::log("drop table user.");
            //    db.simple_execute("DROP TABLE user;");
        }
        else{
            //      Log::log("create table user.");
            simple_execute("CREATE TABLE user("
                              "id   INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,"
                              "user_mail TEXT NOT NULL UNIQUE,"
                              "user_name TEXT  NOT NULL,"
                              "user_pwd  TEXT  NOT NULL);"
                              );
            simple_execute("INSERT INTO user(user_mail,user_name,user_pwd) VALUES('a29uZ193QGZveG1haWwuY29t','a3ZlbGw=','MTIz');");
            //kong_w@foxmail.com kvell 123
        }
        if(table_exist("temp")) {

        }
        else{
            simple_execute("CREATE TABLE temp("
                              "id   INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,"
                              "mail TEXT NOT NULL,"
                              "type TEXT NOT NULL,"
                              "message  TEXT  NOT NULL);"
                              );
//            simple_execute("INSERT INTO temp(user_mail,user_name,user_pwd) VALUES('a29uZ193QGZveG1haWwuY29t','a3ZlbGw=','MTIz');");
        }
        simple_execute("PRAGMA synchronous = NORMAL;");
        //  Log::log("insert data to user.");
        //  db.simple_execute("INSERT INTO user(user_mail,user_name,user_pwd) VALUES('a29uZ193QGZveG1haWwuY29t','a3ZlbGw=','MTIz');");
        return true;
    }

    void sqlite_open(const char *file) {
        open(file);
        //    Log::log("db opened");
    }
    void sqlite_close() {
        close();
        //    Log::log("db closed");
    }
    int sqlite_read(const char *lpszSql,int (*callback)(void*,int,char**,char**),void *data,char **errmsg) {
        int result;
        try {
            //simple_execute("SELECT * FROM user;");
            result=sqlite3_exec(m_db, lpszSql, callback, data, errmsg);
            if(result!=SQLITE_OK)
                throw SqliteError(result);
        } catch (SqliteError &e) {
            std::string msg;// = std::to_string(idx);
            msg += " db read error: ";
            msg += e.what();
            Log::log(msg);
        }
        return result;
    }

    int sqlite_read_data(const char *lpszSql,char ***resultp, int *nrow, int *ncolumn, char **errmsg) {
        int result;
        try {
            //simple_execute("SELECT * FROM user;");
            result=sqlite3_get_table(m_db, lpszSql, resultp, nrow, ncolumn,errmsg);
            if(result!=SQLITE_OK)
                throw SqliteError(result);
        } catch (SqliteError &e) {
            std::string msg;// = std::to_string(idx);
            msg += " db read error: ";
            msg += e.what();
            Log::log(msg);
        }
        return result;
    }

    int sqlite_write(const char *lpszSql,char **errmsg) {
        int result;
        try {
            //simple_execute("UPDATE user SET user_name='a3ZlbGw=',user_pwd='MTIz' WHERE user_mail='a29uZ193QGZveG1haWwuY29t';");
            result=sqlite3_exec(m_db, lpszSql, NULL, NULL, errmsg);
            if(result!=SQLITE_OK)
                throw SqliteError(result);
        } catch (SqliteError &e) {
            std::string msg;// = std::to_string(idx);
            msg += " db write error: ";
            msg += e.what();
            Log::log(msg);
        }
        return result;
    }
protected:
    sqlite3* m_db;
};

// Read Write Lock
//rwlock::Lock lock_;
//Sqlite db;
//unsigned int rw_count_ = 100;
//bool use_lock_ = true;
//unsigned int read_thread_count_ = 1;
//unsigned int write_thread_count_ = 1;

//// read thread
//void read_thread() {
//    rwlock::LockRead _(lock_);
//    for(int i=0;i<100;i++)
//        db.test_read(i);
//}

//// write thread
//void write_thread() {
//    rwlock::LockWrite _(lock_);
//    for(int i=0;i<100;i++)
//        db.test_write(i);
//}

//bool init_sqlite() {
//    Sqlite db;
//    if (db.table_exist("user")) {
//        //    Log::log("drop table user.");
//        //    db.simple_execute("DROP TABLE user;");
//    }
//    else{
//        //      Log::log("create table user.");
//        db.simple_execute("CREATE TABLE user("
//                          "id   INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,"
//                          "user_mail TEXT NOT NULL UNIQUE,"
//                          "user_name TEXT  NOT NULL,"
//                          "user_pwd  TEXT  NOT NULL);"
//                          );
//    }

    //  Log::log("insert data to user.");
    //  db.simple_execute("INSERT INTO user(user_mail,user_name,user_pwd) VALUES('a29uZ193QGZveG1haWwuY29t','a3ZlbGw=','MTIz');");
    //  db.simple_execute("INSERT INTO user(user,age,sex) VALUES('carry',19,0);");
    //  db.simple_execute("INSERT INTO user(user,age,sex) VALUES('simtao',20,1);");
    //  db.simple_execute("INSERT INTO user(user,age,sex) VALUES('three',21,1);");
//    return true;
//}

//int main(int argc, char **argv) {
    //  if (argc != 5) {
    //    std::cout << "[Usage] <rw times> <read thread count> <write thread count> <use lock>" << std::endl;
    //    return 0;
    //  }

    //  rw_count_ = atoi(argv[1]);
    //  read_thread_count_ = atoi(argv[2]);
    //  write_thread_count_ = atoi(argv[3]);
    //  use_lock_ = atoi(argv[4]) == 1 ? true : false;

    //  std::cout << "Read  Write   Times: " << rw_count_ << std::endl;
    //  std::cout << "Read  Thread  Count: " << read_thread_count_ << std::endl;
    //  std::cout << "Write Thread  Count: " << write_thread_count_ << std::endl;
    //  std::cout << "Use Read Write Lock: " << use_lock_ << std::endl;

//    if (!init_sqlite())
//        return 0;

    //  std::vector<std::thread> read_threads_;
    //  std::vector<std::thread> write_threads_;

    //  for (uint32_t i = 0; i < 10; ++i)
    //    read_threads_.push_back(std::thread(read_thread));
    //  for (uint32_t i = 0; i < 10; ++i)
    //    write_threads_.push_back(std::thread(write_thread));

    //  for (auto &td : read_threads_)
    //    td.join();
    //  for (auto &td : write_threads_)
    //    td.join();

//    return 0;
//}
